<script setup>
import utils from '@/assets/scripts/utils'
import { onMounted, ref, inject } from 'vue'
import { EditPen } from '@element-plus/icons-vue'
import apis from '@/assets/apis'
import { ElMessage, ElMessageBox } from 'element-plus'
import store from '../store'

const props = defineProps({
  data: Object,
  columns: Array,
  tables: Array,
  databases: Array,
  editable: Boolean
})

const context = inject('context')

const emits = defineEmits(['close'])

const form = ref([])
const isAdd = ref(false)

const primaryKeyField = ref(null)

// 原始的数据，用于执行数据更新
const originData = {}
let editingTableName

function bytesToHex(bytes) {
  return '0x' + Array.from(bytes)
    .map(byte => {
      // 将每个字节转换为两位十六进制数
      const hex = (byte & 0xFF).toString(16) // 确保字节值在 0-255 之间
      return hex.length === 1 ? '0' + hex : hex // 确保每个字节以两位表示
    }).join('')
}

onMounted(() => {
  let data = props.data
  if (data == null) {
    isAdd.value = true
    data = {}
  }
  if (props.editable) {
    editingTableName = props.tables[0]
  }
  const tables = []
  form.value = props.columns.map((field, idx) => {
    // 添加时，给所有的字段设置值为 null
    let value = isAdd.value ? null : data[field]
    let type = typeof value
    let length = 0
    let isLongText = false
    let isJson = false
    if (type === 'string') {
      length = value.length
      isLongText = length > 40
      if (value[0] === '{' || value[0] === '[') {
        try {
          JSON.parse(value)
          isJson = true
        } catch (e) {
          // ignore
        }
      }
    } else if (Array.isArray(value)) {
      // 数组表示 blob binary 类型
      value = bytesToHex(value)
    } else if (typeof value === 'boolean') {
      // 处理 bool 类型
      value = value ? '1' : '0'
    }

    const tableName = `${props.databases[idx]}.${props.tables[idx]}`
    if (!tables.includes(tableName)) {
      tables.push(tableName)
    }
    return {
      field,
      value,
      type,
      length,
      isLongText,
      tableName,
      isJson,
      formatted: false,
      focused: false,
      key: `${tableName}.${field}`,
      def: {}
    }
  })
  tables.forEach(loadColumns)
})

function updateItemJsonFlag(item) {
  let value = item.value
  if (!value) {
    item.isJson = false
  }
  if (typeof value === 'string') {
    value = value.trim()
  }
  if (value[0] === '{' || value[0] === '[') {
    try {
      JSON.parse(value)
      item.isJson = true
    } catch (e) {
      item.isJson = false
    }
  }
}

function loadColumns(table) {
  apis.dbm.table
    .getColumns(table)
    .then(data => {
      /**
       * COLUMN_COMMENT: "原子服务id"
       * COLUMN_DEFAULT: null
       * COLUMN_KEY: "PRI"
       * COLUMN_NAME: "id"
       * COLUMN_TYPE: "varchar(32)"
       * EXTRA: ""
       * IS_NULLABLE: "NO"
       * @type {{data: *, columns: *, rowsAffected: *}}
       */
      const def = utils.parseDatatable(data[0])
      const temp = {}
      def.data.forEach(item => {
        temp[item.COLUMN_NAME] = item
        const [type, length] = item.COLUMN_TYPE.split('(')
        item.type = type
        item.length = length ? parseInt(length.replace(')', '')) : 0
        item.renderType = getColumnType(item)
        if (item.COLUMN_KEY === 'PRI') {
          primaryKeyField.value = {
            type: type,
            field: item.COLUMN_NAME
          }
        }
      })
      form.value.forEach((item) => {
        if (item.tableName === table) {
          item.def = temp[item.field]
          originData[item.field] = item.value
        }
      })
    }).catch(e => {
    // this.$store.commit('updateGlobalMsg', `错误： ${e.message}`)
    ElMessage.error(e.message)
  }).finally(() => {
    // this.loading = false
  })
}

function getColumnType(def) {
  let type
  switch (def.type) {
    case 'varchar':
    case 'tinytext':
    case 'text':
    case 'mediumtext':
    case 'longtext':
    case 'char':
    case 'json':
    // 以下类型，JSON无法传输，处理成字符串
    case 'double':
    case 'decimal':
    case 'bigint':
      type = 'text'
      break
    case 'tinyint':
    case 'smallint':
    case 'int':
    case 'integer':
    case 'mediumint':
      type = 'number'
      break
    case 'float':
      type = 'float'
      break
    case 'date':
      type = 'date'
      break
    case 'datetime':
      type = 'datetime'
      break
    case 'bit':
    case 'bool':
      type = 'bool'
      break
    default:
      type = 'text'
  }
  return type
}

function setNull(item) {
  if (item.def.IS_NULLABLE === 'YES') {
    item.value = null
  }
}

function doDelete() {
  ElMessageBox.confirm(`确定要删除当前数据吗？`).then(() => {
    const args = {
      database: props.databases[0],
      table: editingTableName,
      key: primaryKeyField.value.field,
      keyValue: originData[primaryKeyField.value.field]
    }

    const values = []
    // 主键的值
    values.push({
      value: args.keyValue,
      field: primaryKeyField.value.field,
      type: primaryKeyField.value.type
    })
    const sql = `DELETE
                 FROM \`${args.database}\`.\`${args.table}\`
                 WHERE \`${args.key}\` = ?`

    // 执行删除
    const connection = store.state.connections.filter(item => item.label === context.environ.connection)[0]
    apis.dbm.connection.execute(connection, args.database, {
      statement: sql,
      args: values
    }).then(() => {
      ElMessage.success('删除成功')
      doClose('delete', args.key, args.keyValue)
    }).catch(e => {
      ElMessage.error(e.message)
    })
  }).catch(() => {
  })
}

function doEdit() {
  const args = {
    database: props.databases[0],
    table: editingTableName,
    key: primaryKeyField.value.field,
    keyValue: originData[primaryKeyField.value.field]
    // changes: form.value.filter(item => {
    //   return item.value !== originData[item.field]
    // }).map(item => {
    //   return {
    //     field: item.field,
    //     value: item.value,
    //     oldValue: originData[item.field]
    //   }
    // })
  }
  const fields = []
  const values = []
  const newData = {}
  form.value.forEach(item => {
    fields.push(`\`${item.field}\`=?`)
    const type = item.def.COLUMN_TYPE.split('(')[0]
    let value = item.value
    values.push({
      value: value,
      field: item.field,
      type: type
    })
    newData[item.field] = item.value
  })
  // 主键的值
  values.push({
    value: args.keyValue,
    field: primaryKeyField.value.field,
    type: primaryKeyField.value.type
  })
  const sql = `UPDATE \`${args.database}\`.\`${args.table}\`
               SET ${fields.join(', ')}
               WHERE \`${args.key}\` = ?`

  // 执行编辑
  const connection = store.state.connections.filter(item => item.label === context.environ.connection)[0]
  apis.dbm.connection.execute(connection, args.database, {
    statement: sql,
    args: values
  }).then(() => {
    ElMessage.success('更新成功')
    doClose('edit', args.key, args.keyValue, newData)
  }).catch(e => {
    ElMessage.error(e.message)
  })
}

function doAdd() {
  const args = {
    database: props.databases[0],
    table: editingTableName,
    key: primaryKeyField.value.field,
    keyValue: originData[primaryKeyField.value.field]
    // changes: form.value.filter(item => {
    //   return item.value !== originData[item.field]
    // }).map(item => {
    //   return {
    //     field: item.field,
    //     value: item.value,
    //     oldValue: originData[item.field]
    //   }
    // })
  }
  const fields = []
  const placeholders = []
  const values = []
  const newData = {}
  form.value.forEach(item => {
    fields.push(item.field)
    placeholders.push('?')
    const type = item.def.COLUMN_TYPE.split('(')[0]
    let value = item.value
    values.push({
      value: value,
      field: item.field,
      type: type
    })
    newData[item.field] = item.value
  })
  const sql = `INSERT INTO \`${args.database}\`.\`${args.table}\`
                 (${fields.join(', ')})
               VALUES (${placeholders.join(', ')})`

  // 执行编辑
  const connection = store.state.connections.filter(item => item.label === context.environ.connection)[0]
  apis.dbm.connection.execute(connection, args.database, {
    statement: sql,
    args: values
  }).then((result) => {
    // 添加数据的自增主键
    const insertId = result[0].insertId
    if (insertId !== -1) {
      newData[args.key] = insertId
    }
    ElMessage.success('添加成功')
    doClose('add', args.key, args.keyValue, newData)
  }).catch(e => {
    ElMessage.error(e.message)
  })
}

function doClose(action, key, id, data) {
  emits('close', {
    action,
    key,
    id,
    data
  })
}

function formatJson(item) {
  if (item.formatted) {
    item.value = JSON.stringify(JSON.parse(item.value))
  } else {
    item.value = JSON.stringify(JSON.parse(item.value), null, 2)
  }
  item.formatted = !item.formatted
}
</script>

<template>
  <div class="row-detail">
    <div class="edit-flag" :class="{editable}">
      <edit-pen></edit-pen>
      <span v-if="!editable">不可编辑</span>
      <span v-else-if="isAdd">添加</span>
      <span v-else>可编辑</span>
    </div>
    <el-form label-width="200">
      <el-form-item v-for="item in form" :key="item.key" :label="item.field">
        <div class="input-control--container">
          <div :class="`data-type--${item.def.renderType}`">
            <el-input class="input-control" size="small" :autosize="{minRows: 1, maxRows: 16}" type="textarea"
                      v-model="item.value" :maxlength="item.def.length" show-word-limit
                      @focus="item.focused=true" @blur="item.focused=false" :readonly="!editable"
                      @change="updateItemJsonFlag(item)"
                      v-if="['text'].includes(item.def.renderType)"></el-input>
            <el-input class="input-control" size="small" :autosize="{minRows: 1, maxRows: 16}" type="textarea"
                      v-model.number="item.value" :maxlength="item.def.length" show-word-limit
                      @focus="item.focused=true" @blur="item.focused=false" :readonly="!editable"
                      @change="updateItemJsonFlag(item)"
                      v-else-if="['number', 'float', 'bool'].includes(item.def.renderType)"></el-input>

            <!--            <el-input-number class="input-control" size="small" autosize type="textarea" v-model="item.value"-->
            <!--                             @focus="item.focused=true" @blur="item.focused=false" :readonly="!editable"-->
            <!--                             :controls="false" :step="1"-->
            <!--                             v-if="item.def.renderType === 'number'"></el-input-number>-->

            <!--            <el-input-number class="input-control" size="small" autosize type="textarea" v-model="item.value"-->
            <!--                             @focus="item.focused=true" @blur="item.focused=false" :readonly="!editable"-->
            <!--                             :controls="false" :precision="6"-->
            <!--                             v-if="item.def.renderType === 'float'"></el-input-number>-->

            <el-date-picker class="input-control" size="small" autosize type="date" v-model="item.value"
                            @focus="item.focused=true" @blur="item.focused=false" :readonly="!editable"
                            v-else-if="item.def.renderType === 'date'" value-format="YYYY-MM-DD"></el-date-picker>

            <el-date-picker class="input-control" size="small" autosize type="datetime" v-model="item.value"
                            @focus="item.focused=true" @blur="item.focused=false" :readonly="!editable"
                            v-else-if="item.def.renderType === 'datetime'"
                            value-format="YYYY-MM-DD HH:mm:ss"></el-date-picker>

            <!--            <el-switch class="input-control" size="small" autosize v-model="item.value"-->
            <!--                       @focus="item.focused=true" @blur="item.focused=false" :readonly="!editable"-->
            <!--                       v-if="item.def.renderType === 'bool'"></el-switch>-->
          </div>

          <div class="value-null" v-if="item.value === null && !item.focused">NULL</div>
          <div class="column-desc">
            <span class="type">{{ item.def.COLUMN_TYPE }}</span>
            <span class="nullable" :class="{is_nullable: item.def.IS_NULLABLE === 'YES'}" @click="setNull(item)"
                  title="点击设为 NULL">NULLABLE</span>
            <span class="primary" v-if="item.def.COLUMN_KEY === 'PRI'">主键</span>
            <span class="json-flag" v-if="item.isJson" title="点击切换格式化" @click="formatJson(item)">JSON</span>
            <span class="comment" :title="item.def.COLUMN_COMMENT">{{ item.def.COLUMN_COMMENT }}</span>
          </div>
        </div>
      </el-form-item>
    </el-form>
    <div class="row-editor--footer">
      <el-button type="danger" @click="doDelete" v-if="editable && !isAdd">删除</el-button>
      <el-button type="primary" @click="doEdit" v-if="editable && !isAdd">修改</el-button>
      <el-button type="primary" @click="doAdd" v-if="editable && isAdd">添加</el-button>
      <el-button @click="doClose()">关闭</el-button>
    </div>
  </div>
</template>

<style scoped lang="less">
.row-detail {
  position: relative;
  height: 100%;
  word-break: break-all;
}

.edit-flag {
  width: 60px;
  height: 60px;
  position: absolute;
  left: 0;
  top: 0;
  z-index: 0;
  color: rgba(209, 209, 209, 0.51);
  font-size: 13px;
  user-select: none;
}

.edit-flag.editable {
  color: rgba(49, 165, 49, 0.51);
}

.el-form {
  width: 100%;
  padding-right: 10px;
}

.column-desc {
  font-size: 12px;
  gap: 10px;
  display: flex;
  line-height: 1.5;
}

.type {
  color: #2d69c7;
  text-transform: uppercase;
  flex: 0 0 auto;
}

.primary {
  color: rebeccapurple;
}

.nullable {
  color: #cccccc;
  flex: 0 0 auto;

  &.is_nullable {
    color: #5aaa5a;
    cursor: pointer;
  }
}

.comment {
  color: #888888;
  word-break: keep-all;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}

.value-null {
  position: absolute;
  left: 0;
  top: 4px;
  padding: 0 0 0 10px;
  line-height: 28px;
  color: #cccccc;
}

//.data-type--bool + .value-null {
//  left: 40px;
//}

.input-control--container {
  width: 100%;
}

.input-control {
  font-size: 12px;
}

.el-form {
  height: calc(100% - 30px);
  overflow: auto;
  padding-bottom: 200px;
  box-sizing: border-box;
}

.row-editor--footer {
  position: absolute;
  left: 0;
  width: 100%;
  bottom: -20px;
  padding: 10px;
  text-align: right;
  background-color: #ffffff;
  z-index: 2;
}

.el-form-item {
  margin-bottom: 10px;
}

.el-textarea {
  // 40 个字
  // 40 * 12 = 480
  min-width: 500px;
}

.json-flag {
  cursor: pointer;
  color: coral;
}
</style>
